home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Games / xsnake / xsnake.c < prev   
C/C++ Source or Header  |  1995-05-03  |  45KB  |  1,577 lines

  1. /***************************************************************************
  2. *    xsnake - X windows 2D game, make the snake eat the rats.
  3. *    Copyright (C) 1993 Dave K. Redford.
  4. *
  5. *    This program is free software; you can redistribute it and/or modify
  6. *    it under the terms of the GNU General Public License as published by
  7. *    the Free Software Foundation; either version 2 of the License, or
  8. *    (at your option) any later version.
  9. *
  10. *    This program is distributed in the hope that it will be useful,
  11. *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. *    GNU General Public License for more details.
  14. *
  15. *    You should have received a copy of the GNU General Public License
  16. *    along with this program; if not, write to the Free Software
  17. *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18. *
  19. *   
  20. *    Questions about this software should be addressed to:
  21. *
  22. *    dredf.lax1b@xerox.com     (can only handle text)
  23. *
  24. ***************************************************************************/
  25.  
  26. /* xsnake.c  - D.Redford - 1/5/91 */
  27.  
  28. #include <stdio.h>
  29. #include <ctype.h>
  30. #include <stdlib.h>
  31.  
  32. #include <X11/Xlib.h>
  33. #include <X11/Xatom.h>
  34. #include <X11/Xutil.h>
  35.  
  36. static char sccs_id[] = "@(#) xsnake.c v1.4 dkr 93/04/19 20:25:37";
  37.  
  38. /*---------------------------------------------------------------------------*/
  39. /* macros */
  40. #ifndef TRUE
  41. # define  TRUE    1
  42. #endif /*TRUE*/
  43. #ifndef FALSE
  44. # define  FALSE    0
  45. #endif /*FALSE*/
  46. #define MIN(x,y) ((x<y) ? (x) : (y))
  47. #define MAX(x,y) ((x>y) ? (x) : (y))
  48.  
  49. /*---------------------------------------------------------------------------*/
  50. /* defines */
  51.  
  52. /* window control */
  53. /* width vs height aspect ratios */
  54. #define W_ASPECT    (5.0/4.0)    /* width is 5/4's height */
  55. #define H_ASPECT    (4.0/5.0)    /* height is 4/5's width */
  56.  
  57. #define TEXT_COLUMNS    52        /* num text cols wanted */
  58. #define TEXT_LINES    3        /* num lines */
  59.  
  60. #define MWM_BORDER    14        /* ballpark Mwm title bar width */
  61.  
  62. /* plot control */
  63. #define GRID_SIZE    100        /* pseudo screen size */
  64.                     /* must a multiple of 10 */
  65. #define BOX_WIDTH    (GRID_SIZE/25)    /* size of boxes 1/25th of grid */
  66. #define BOX_HEIGHT    BOX_WIDTH
  67.  
  68. #define MIN_TAIL    (GRID_SIZE/2)    /* min tail length */
  69. #define MAX_TAIL    (GRID_SIZE*5)    /* max tail length */
  70. #define DEF_TAIL    (GRID_SIZE*2)    /* default tail length */
  71. #define INC_TAIL    (GRID_SIZE/2)    /* tail length granularity */
  72. #define NUM_TAIL_INCS    (MAX_TAIL / INC_TAIL) /* number of tail lengths */
  73.  
  74. /* used to access high score table for this tail length */
  75. #define SCORE_HIGH    (high_scores[tail_max / INC_TAIL - 1])
  76.  
  77. #define BOX_WARNING    (GRID_SIZE/4)    /* warning lead time for boxes that */
  78.                     /* remain - allows you to move 1/4  */
  79.                     /* across the grid before remain  */
  80. #define NUM_RAND_BOXES    (GRID_SIZE/6)    /* ballpark random box figure */
  81.  
  82. /* game control */
  83. #define PAUSE_1_SEC    (1000)        /* 1 second in msecs */
  84.             /* pause value needed to cross grid in 7 seconds */
  85. #define PAUSE        ((PAUSE_1_SEC * 7) / GRID_SIZE)
  86.  
  87. #define PAUSE_FLASH    (PAUSE_1_SEC / 4)    /* banner flash rate = 25Hz */
  88.  
  89. /* game states */
  90. #define S_INIT        0    /* game init */
  91. #define S_EXIT        1    /* close down and exit */
  92. #define S_RUN        2    /* running */
  93. #define S_SPACE        3    /* wait for space key - after DEAD */
  94. #define S_CONT        4    /* wait for continue  - after space wait */
  95. #define S_CONFIG    5    /* wait for config type */
  96. #define S_SET_KEY    6    /* config keys */
  97. #define S_SET_LEN    7    /* config length */
  98.  
  99. /* default key values */
  100. #define KEY_UP        145    /* up */
  101. #define KEY_DOWN    146    /* down */
  102. #define KEY_LEFT    144    /* left */
  103. #define KEY_RIGHT    143    /* right */
  104.  
  105. #define KEY_SPACE    64    /* sp - continue key */
  106. #define KEY_CR        35    /* cr - configure key */
  107. #define KEY_ESC        48    /* esc - exit key */
  108. #define KEY_L        45    /* l - config length key */
  109. #define KEY_K        44    /* k - config keys key */
  110. #define KEY_LESS    58    /* < - length decrease key */
  111. #define KEY_MORE    59    /* > - length increase key */
  112.  
  113. /* directions as used by key_list[] and key_dir[] which must match */
  114. #define KEY_DIR_UP    0    /* up */
  115. #define KEY_DIR_DOWN    1    /* down */
  116. #define KEY_DIR_LEFT    2    /* left */
  117. #define KEY_DIR_RIGHT    3    /* right */
  118.  
  119.  
  120. /* colour control - must match colour[] and colour_val[] */
  121. #define COL_WBACK    (colour_val[0])    /* window background */
  122. #define COL_DBACK    (colour_val[1])    /* draw area background */
  123. #define COL_BORDER    (colour_val[2])    /* border */
  124. #define COL_BOX_LIVE    (colour_val[3])    /* live box */
  125. #define COL_BOX_WARN    (colour_val[4])    /* warning box */
  126. #define COL_BOX_DEAD    (colour_val[5])    /* dead box */
  127. #define COL_SNAKE    (colour_val[6])    /* snake */
  128. #define COL_FONT_NORM    (colour_val[7])    /* font normal intensity */
  129. #define COL_FONT_HIGH    (colour_val[8])    /* font high */
  130. #define COL_FONT_CHNG    (colour_val[9])    /* font for changing lenght */
  131.  
  132. #define CONF_FILE    ".xsnake.cnf"
  133.  
  134. /*---------------------------------------------------------------------------*/
  135. /* globals */
  136.  
  137. /* window control */
  138. static Window win;        /* game window */
  139. static Display *dpy;        /* main display */
  140. static Screen *scrn;        /* screen */
  141. static GC gc;            /* graphics context */
  142. static int winW, winH;         /* window width and height */
  143. static int old_winW, old_winH;    /* previous for resizing */
  144.  
  145. static char banner_string[TEXT_COLUMNS];/* current banner string */
  146.  
  147. static char *config_file;    /* pointer to config file name */
  148.  
  149. /* colours - must match #define COL_*/
  150. static char *colour[] =
  151. {
  152.   "midnightblue",    /* window background */
  153.   "black",        /* draw area background */
  154.   "darkturquoise",    /* border */
  155.   "yellow",        /* live box */
  156.   "red",        /* warning box */
  157.   "maroon",        /* dead box */
  158.   "gold",        /* snake */
  159.   "lightblue",        /* font norm */
  160.   "wheat",        /* font high */
  161.   "springgreen"        /* font changing length */
  162. };
  163.  
  164. #define NUM_COLS    (sizeof(colour)/sizeof(char *))
  165.  
  166. static int colour_val[NUM_COLS];/* values for above */
  167.  
  168. /* font control */
  169. static char *font_list[] =    /* list of font names to use */
  170. {
  171.   "*-times-bold-r-normal--8-*",
  172.   "*-times-bold-r-normal--11-*",
  173.   "*-times-bold-r-normal--14-*",
  174.   "*-times-bold-r-normal--17-*",
  175.   "*-times-bold-r-normal--20-*",
  176.   "*-times-bold-r-normal--24-*",
  177.   "*-times-bold-r-normal--25-*",
  178. };
  179.  
  180. #define NUM_FONTS (sizeof(font_list)/sizeof(char *))
  181.  
  182. #define DEF_FONT 2        /* default font rom14 */
  183.  
  184. static XFontStruct *font_info[NUM_FONTS];    /* font info for each font */
  185.  
  186. static int
  187.   font_widths[NUM_FONTS],    /* all the max widths */
  188.   font_heights[NUM_FONTS],    /* all the max heights */
  189.   current_font;            /* current selected font */
  190.  
  191. /* plot control */
  192. static int drawx,draww,        /* drawing area x-pos and width */
  193.        drawy,drawh,        /*              y-pos and height */
  194.        pointw,pointh,    /* point size for scaling */
  195.        pset_inhibit,    /* flag for pset */
  196.        redraw_skip;        /* flag for redrawing - when resizing */
  197.  
  198. static float scalex,         /* x and y scaling to map grid to */
  199.          scaley;        /* drawing area */
  200.  
  201. static int 
  202.   grid[GRID_SIZE][GRID_SIZE],    /* pseudo screen - plotting area */
  203.   current_foreground,        /* current foreground colour */
  204.   col_max_len;            /* current max length colour */
  205.  
  206. /* event flags */
  207. static int obscured,         /* true when window is obscured */
  208.        suspend;        /* true when game should stop - due to */
  209.                 /* obscured window or mouse out of window */
  210.  
  211. /* game control */
  212. static int ring[MAX_TAIL][2];    /* tail x,y history */
  213.  
  214. static int
  215.   last_key_hit,        /* last key press */
  216.   game_state;        /* current game state */
  217.  
  218. static int key_list[] =        /* direction keys - set to defaults */
  219. {                /* must match KEY_DIR_xxxxx */
  220.   KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT
  221. };
  222.  
  223. static char *key_dir[] =    /* direction strings */
  224. {                /* must match KEY_DIR_xxxxx */
  225.   "UP", "DOWN", "LEFT", "RIGHT"
  226. };
  227.  
  228. static int
  229.   head_ring,        /* head ring postion */
  230.   tail_ring,        /* tail ring postion */
  231.   head_x,        /* head x coord */
  232.   head_y,        /* head y coord */
  233.   xinc,            /* x increment - for direction */
  234.   yinc,            /* y increment - for direction */
  235.  
  236.             /* NB values and delays are all in grid positions */
  237.   box_delay,        /* delay before new box should appear */
  238.   box_value,        /* value of box when hit */
  239.   box_remain,        /* box will remain flag */
  240.   box_life,        /* time box will stay alive */
  241.   box_x_pos,        /* live box x pos */
  242.   box_y_pos,        /* live box y pos */
  243.  
  244.   tail_delay,        /* tail grow amount - decrements as tail grows */
  245.   tail_max,        /* max tail length */
  246.   snake_len,        /* current snake length */
  247.   score,        /* current score */
  248.   score_last,        /* last score */
  249.   high_scores[NUM_TAIL_INCS]; /* high score table 1 for each max length */
  250.  
  251. /*---------------------------------------------------------------------------*/
  252. static char *help_text[] =
  253. {
  254. "Your task is to move the snake around the screen and try to hit the yellow",
  255. "or red boxes.",
  256. "",
  257. "When you hit a box your score will increase, the box will disappear and your",
  258. "tail will grow longer. If the maximum tail length is exceeded your tail will",
  259. "snap off leaving a dead tail.",
  260. "",
  261. "A yellow box will appear randomly and remain for a random time, if you ",
  262. "don't hit it in time it will either disappear or die (maroon colour).",
  263. "",
  264. "If a box is going to die it will turn red to warn you, if you hit it during",
  265. "this time you will gain ten points.",
  266. "Conversely, if you allow a box to disappear you will lose ten points.",
  267. "",
  268. "If you hit anything other than a yellow or red box (including your own tail,",
  269. "dead tails, the walls or dead boxes) the game will end.",
  270. "",
  271. "The default direction keys are:-  q   Up.        ,   Left.",
  272. "                                  a   Down.      .   Right.",
  273. "",
  274. "The tail length and direction keys can be changed within the game by entering",
  275. "configuration mode at the appropriate prompt.",
  276. "",
  277. "The length can also be changed at game start with the -l option.",
  278. "",
  279. "The window size can be changed by the mouse or by selecting a scale at game",
  280. "start with the -s option.",
  281. "",
  282. "The keys, high scores, maximum tail length, and window size are recorded",
  283. "on file so they can be restored if you play again.",
  284. "",
  285. "There is a different high score recorded for each maximum tail length value.",
  286. "",
  287. "The game will pause if the mouse is outside the game window or if the window",
  288. "is obscured.",
  289. "",
  290. "At any time the game can be exited by pressing the escape key.",
  291. "",
  292. 0x00
  293. };
  294.  
  295. /*---------------------------------------------------------------------------*/
  296. /* set keys, font and max tail to defaults, and set high scores to zero */ 
  297. static void set_defaults()
  298. {
  299.   int i;
  300.  
  301.   current_font = DEF_FONT;    /* set the defaults */
  302.   tail_max = DEF_TAIL;
  303.  
  304.   for (i=0; i<NUM_TAIL_INCS; i++)    /* for all high scores */
  305.     high_scores[i] = 0;
  306.  
  307.   key_list[0] = KEY_UP;     key_list[1] = KEY_DOWN;
  308.   key_list[2] = KEY_LEFT;     key_list[3] = KEY_RIGHT;
  309. }
  310.  
  311. /*---------------------------------------------------------------------------*/
  312. /* read the four direction keys, max tail length, font No. and the high score
  313.  * table from the file - if the read fails then set them to default values 
  314.  * but keep the filename for writing to later */
  315. static void read_file(fp)
  316. FILE *fp;
  317. {
  318.   int i, fail = TRUE;
  319.  
  320.   if (fscanf (                    /* keys and tail */
  321.               fp, "%d %d %d %d %d %d", 
  322.               &key_list[0], &key_list[1], &key_list[2], 
  323.               &key_list[3], &tail_max, ¤t_font) == 6)
  324.   {
  325.     fail = FALSE;
  326.     for (i=0; i<NUM_TAIL_INCS; i++)        /* high score table */
  327.     {
  328.       if (fscanf(fp, "%d", &high_scores[i]) != 1)
  329.       {
  330.     fail = TRUE;
  331.     break;
  332.       }
  333.     }
  334.   }
  335.  
  336.   if (fclose(fp) != 0)            /* finished with file */
  337.   {
  338.     perror("Can't close file");
  339.     exit(1);
  340.   }
  341.  
  342.   if (fail == TRUE)            /* read failed */
  343.     set_defaults();
  344. }
  345.  
  346. /*---------------------------------------------------------------------------*/
  347. /* scan the PATH looking for the configuration file. The path may not contain
  348.  * the users home directory so add it. If the file is found record the name
  349.  * for writing to later.
  350.  * If the path is duff or the file isn't found set the default values for
  351.  * tail length, font, keys and high scores. */
  352. static void find_file()
  353. {
  354.   char *p1, *path, *nextpath, *testfile, *name;
  355.   FILE *fp;
  356.   int length, namelen;
  357.  
  358.   config_file = NULL;                /* use this as a flag */
  359.   length = strlen(CONF_FILE);
  360.  
  361.   if ((p1 = getenv("PATH")) != NULL)        /* if a path variable set */
  362.   {
  363.                         /* get login name & length */
  364.     namelen = (name = getlogin()) == NULL ? 0 : strlen(name);
  365.  
  366.                         /* grab space for path */
  367.     if ((path = malloc(strlen(p1) + namelen + 6)) != NULL)
  368.     {
  369.       if (namelen >0)                /* if got name add home dir */
  370.     sprintf(path, "/u/%s:%s", name, p1);
  371.       else
  372.         strcpy(path, p1);            /* else just use PATH */
  373.                         
  374.       nextpath = strtok(path, ":");        /* for all directories */
  375.       while(nextpath != NULL)
  376.       {                    /* grab space to build file name */
  377.     if ((testfile = malloc(strlen(nextpath) + length + 4)) != NULL)
  378.  
  379.     {                    /* create name */
  380.       sprintf(testfile, "%s/%s", nextpath, CONF_FILE);
  381.  
  382.       if ((fp = fopen(testfile, "r")) != NULL)
  383.       {                    /* it opened */
  384.         config_file = testfile;        /* record file name */
  385.         break;                
  386.       }
  387.     }
  388.     else
  389.     {
  390.       printf("find file/1 malloc failed\n");/* oh dear */
  391.       exit(1);
  392.     }
  393.     free(testfile);                /* didn't open */
  394.     nextpath = strtok(NULL,":");        /* try next directory */
  395.       }
  396.       free(path);                /* free this */
  397.     }
  398.     else
  399.     {
  400.       printf("find file/2 malloc failed\n");    /* oh dear */
  401.       exit(1);
  402.     }
  403.   } /* no PATH variable */
  404.  
  405.   if (config_file != NULL)            /* found a file */
  406.     read_file(fp);                /* try and read it */
  407.   else
  408.     set_defaults();                /* no file */
  409. }
  410.  
  411. /*---------------------------------------------------------------------------*/
  412. /* save the four directions keys, the max tail length, the current font No.,
  413.  * and the high score table in the config file. If the config file wasn't 
  414.  * opened for read succesfully at prog start then create one in the users 
  415.  * home directory. if the write fails just forget it. */
  416. static void file_save()
  417. {
  418.   FILE *fp = NULL;
  419.   int fail, i;
  420.   char *ptr, *name;
  421.  
  422.   if (config_file == NULL)        /* if file wasn't found or was duff */
  423.   {
  424.     if ((name = getlogin()) == NULL)    /* get user name */
  425.       return;                /* forget it */
  426.  
  427.     if ((ptr = malloc(strlen(CONF_FILE) + strlen(name) + 1)) != NULL)
  428.     {
  429.                     /* create file name */
  430.       sprintf(ptr, "/u/%s/%s", name, CONF_FILE);
  431.  
  432.       if ((fp = fopen(ptr, "wc")) != NULL)    /* create file */
  433.     config_file = ptr;            /* yes - use it */
  434.     }
  435.     else
  436.     {
  437.       printf("file save malloc failed\n");
  438.       exit(1);
  439.     }
  440.   }
  441.   else                    /* file was read at startup */
  442.     fp = fopen(config_file, "w");    /* open it */
  443.  
  444.   if (fp != NULL)            /* if got a file to use */
  445.   {
  446.     fail = TRUE;            /* write keys and tail */
  447.     if (fprintf(fp, "%d %d %d %d %d %d ",
  448.         key_list[0], key_list[1], key_list[2],
  449.         key_list[3], tail_max, current_font) > 0)
  450.     {
  451.       fail = FALSE;
  452.       for (i=0; i<NUM_TAIL_INCS; i++)    /* write high score table */
  453.       {
  454.     if (fprintf(fp, "%d ", high_scores[i]) < 0)
  455.     {
  456.       fail = TRUE;
  457.       break;
  458.     }
  459.       }
  460.     }
  461.  
  462.     if (fclose(fp) != 0)        /* done with it */
  463.     {
  464.       perror("Can't close file");
  465.       exit(1);
  466.     }
  467.   }
  468. }
  469.  
  470. /*---------------------------------------------------------------------------*/
  471. /* seed random num generator to the current time */
  472. seed_rnd()
  473. {
  474.   long ltime;
  475.  
  476.   time (<ime);                     /* get seconds */
  477.   srand48 (ltime % 32767);           /* reduce and pass */
  478. }
  479.  
  480. /*---------------------------------------------------------------------------*/
  481. /* returns rnd num between x and y */
  482. static int get_rnd(x,y)
  483. int x,y;
  484. {
  485.   int mod;
  486.  
  487.   mod = y - x + 1;            /* get number of values required */
  488.   return (lrand48() % mod + x);        /* get rnd num + add in range */
  489. }
  490.  
  491. /*---------------------------------------------------------------------------*/
  492. /* set foreground colour - if not already set to that colour */
  493. static void foreground(col)
  494. int col;
  495. {
  496.   if (current_foreground != col)    /* is it same */
  497.   {
  498.     current_foreground = col;        /* no - set it */
  499.     XSetForeground(dpy, gc, col);
  500.   }
  501. }
  502.  
  503. /*---------------------------------------------------------------------------*/
  504. /* print string and number at col,line - in current colour */
  505. static void disp_string_num(str, num, col,line)
  506. char *str;
  507. int num,col,line;
  508. {
  509.   char *s1[TEXT_COLUMNS +2];
  510.  
  511.   sprintf(s1, "%s% .4d", str, num);    /* blank before dot is for sign */
  512.  
  513.   XDrawImageString(dpy, win, gc,
  514.        font_widths[current_font] * col, font_heights[current_font] * line,
  515.        s1, strlen(s1));
  516.   XSync(dpy,0);
  517. }
  518.  
  519. /*---------------------------------------------------------------------------*/
  520. /* print score at col 1 line 1 */
  521. static void update_score()
  522. {
  523.   foreground(COL_FONT_NORM);
  524.   disp_string_num("Score :", score, 1,1);
  525. }
  526.  
  527. /*---------------------------------------------------------------------------*/
  528. /* print length at col 1 line 2 */
  529. static void update_length()
  530. {
  531.   foreground(COL_FONT_NORM);
  532.   disp_string_num("Length:", snake_len, 1,2);
  533. }
  534.  
  535. /*---------------------------------------------------------------------------*/
  536. /* print last score at col 16 line 1 */ 
  537. static void update_last()
  538. {
  539.   foreground(COL_FONT_NORM);
  540.   disp_string_num("Last Score:", score_last, 16,1);
  541. }
  542.  
  543. /*---------------------------------------------------------------------------*/
  544. /* print high score at col 35 line 1 */
  545. static void update_high()
  546. {
  547.   foreground(COL_FONT_NORM);
  548.   disp_string_num("High Score:", SCORE_HIGH, 35,1);
  549. }
  550.  
  551. /*---------------------------------------------------------------------------*/
  552. /* print max length at col 35 line 2 */
  553. static void update_max()
  554. {
  555.   foreground(col_max_len);
  556.   disp_string_num("Max Length:", tail_max, 35,2);
  557. }
  558.  
  559. /*---------------------------------------------------------------------------*/
  560. /* print string in middle of line 3 - in highlight colour */
  561. static void banner_prim(s1)
  562. char *s1;
  563. {
  564.   int x;
  565.  
  566.   foreground(COL_FONT_HIGH);
  567.                 /* centre the text */
  568.   x = (winW - XTextWidth(font_info[current_font], s1, strlen(s1)))/2;
  569.   XDrawImageString(dpy, win, gc, x, font_heights[current_font] * 3, 
  570.                             s1, strlen(s1));
  571.   XSync(dpy,0);
  572. }
  573.  
  574. /*---------------------------------------------------------------------------*/
  575. /* blank the banner line */
  576. static void banner_blank()
  577. {
  578.   char *blank;
  579.   int i;
  580.                     /* grab some string space */
  581.   if ((blank = malloc(TEXT_COLUMNS)) != NULL)
  582.   {
  583.     for (i=0; i<TEXT_COLUMNS; i++)    /* make it spaces */
  584.       blank[i] = ' ';
  585.  
  586.     blank[TEXT_COLUMNS -1] = '\0';    /* terminate */
  587.  
  588.     banner_prim(blank);            /* overwrite old with blank */
  589.     free(blank);            /* free off */
  590.   }
  591.   else
  592.   {
  593.     printf("banner blank malloc failed\n");
  594.     exit(1);
  595.   }
  596. }
  597.  
  598. /*---------------------------------------------------------------------------*/
  599. /* print string on banner line - erasing the previous string - if any */
  600. static void banner(s1)
  601. char *s1;
  602. {
  603.   if (strlen(s1) >= TEXT_COLUMNS)    /* will it fit */
  604.   {
  605.     printf("banner string '%s' too long\n", s1);
  606.     exit(1);
  607.   }
  608.   strcpy(banner_string, s1);    /* save banner string - for redraws */
  609.  
  610.   banner_blank();    /* blank the line */
  611.   banner_prim(s1);    /* now draw the string */
  612. }
  613.  
  614. /*---------------------------------------------------------------------------*/
  615. /* flash the banner string */
  616. static void flash_banner()
  617. {
  618.   int i;
  619.  
  620.   for (i=0; i<3; i++)        /* flash 3 times */
  621.   {
  622.     nap(PAUSE_FLASH);        /* wait */
  623.     banner_blank();        /* blank it */
  624.     nap(PAUSE_FLASH);        /* wait */
  625.     banner(banner_string);    /* redraw it */
  626.   }
  627.   sleep(1);            /* show it for a 1 second */
  628. }
  629.  
  630. /*---------------------------------------------------------------------------*/
  631. /* map a grid coord - to the drawing area (assumes grid is the smaller).
  632.  *
  633.  * NB. If scale = 2.4,  then 99 * 2.4 = 237.6 and 100 * 2.4 = 240 thus 2.4 
  634.  * points on the draw area are missed.
  635.  * To correct this round down the x,y coords by the scale/2 and round up the 
  636.  * width and height by scale/2. This fills in the gap correctly if the grid 
  637.  * is 100 and the window size is >= 170 pixels.
  638.  *
  639.  * Unfortunately - for the sizes of window we are using with fonts from rom8
  640.  * to rom29 the best rounding values have to be found by trial and error.
  641.  * They seem to be 3.2 for x coords and 4 for the y - this prevents too much 
  642.  * overlap spoiling the accuracy of plotting.
  643.  *
  644.  * For speed the point width and height are set in set_scales().
  645.  */
  646. static void pscale(x,y, px,py)
  647. int x,y, *px,*py;
  648. {
  649.   float fx,fy;
  650.  
  651.   fx = x * scalex + drawx;    /* map x coord to scale & add draw x offset */
  652.   fy = y * scaley + drawy;    /* map y coord to scale & add draw y offset */
  653.  
  654.   *px = (int)(fx - scalex/3.2);    /* round the new points down */
  655.   *py = (int)(fy - scaley/4.0);
  656. }
  657.  
  658. /*---------------------------------------------------------------------------*/
  659. /* set the point in the given colour - draws on screen and maps it into the
  660.  * grid */
  661. static void pset(x,y,col)
  662. int x,y,col;
  663. {
  664.   int px,py,pw,ph;
  665.  
  666.   grid[x][y] = col;        /* set grid used */
  667.  
  668.   if (pset_inhibit)        /* if shouldn't draw exit */
  669.     return;            /* see set_random_boxes() */
  670.  
  671.   pscale(x,y, &px,&py);        /* scale the coord for drawing */
  672.  
  673.   foreground(col);        /* set colour */
  674.   XFillRectangle(dpy,win,gc,    px, py, pointw, pointh);/* draw it */
  675.   XSync(dpy,0);
  676. }
  677.  
  678. /*---------------------------------------------------------------------------*/
  679. /* if grid coord is not BLANK then return true */
  680. static int ptest(x,y)
  681. int x,y;
  682. {
  683.   return (grid[x][y] != COL_DBACK);
  684. }
  685.  
  686. /*---------------------------------------------------------------------------*/
  687. /* set the grid scale values so that we get a best fit for a 4/5 ratio 
  688.  * rectangle in the window size provided */
  689. static void set_scales()
  690. {
  691.   int width, height, text_height;
  692.  
  693.   text_height = font_heights[current_font] * TEXT_LINES + MWM_BORDER;
  694.  
  695.   height = winH - text_height;        /* real height available */
  696.   width = winW;                /* real width */
  697.  
  698.   if (width * H_ASPECT > height)    /* can we use width */
  699.   {
  700.     if (height * W_ASPECT > width)    /* can we use height */
  701.     {
  702.       printf("SCALE Failed\n");        /* both failed */
  703.       exit(1);                /* shouldn't happen */
  704.     }
  705.     else
  706.       width = height * W_ASPECT;    /* use height */
  707.   }
  708.   else
  709.     height = width * H_ASPECT;        /* use width */
  710.  
  711.   /* set draw bounds info */
  712.   drawx = 0;            /* x start */
  713.   draww = width;        /* width */
  714.   drawy = text_height;        /* y start */
  715.   drawh = height;        /* height */
  716.  
  717.   /* suss scaling to our grid size */
  718.   scalex = draww / (float)GRID_SIZE;
  719.   scaley = drawh / (float)GRID_SIZE;
  720.  
  721.   /* set point size - see pscale() */
  722.   pointw = (int)(scalex + scalex/3.2);
  723.   pointh = (int)(scaley + scaley/4.0);
  724. }
  725.  
  726. /*---------------------------------------------------------------------------*/
  727. /* if the window size has changed choose a font that best fits the new
  728.  * width using TEXT_COLUMNS for number of chars required.
  729.  */
  730. static void choose_font()
  731. {
  732.   int i, height;
  733.  
  734.   if (winW != old_winW || winH != old_winH)
  735.   {
  736.     redraw_skip = 1;    /* since a resize will generate a new set of */
  737.             /* config and expose events - skip the first redraw */
  738.             /* and use the last one - see redraw_screen() */
  739.     current_font = -1;
  740.  
  741.     for (i=0; i<NUM_FONTS; i++)    /* from smallest font to largest */
  742.     {                /* if it fits */
  743.       if (font_widths[i] * TEXT_COLUMNS <= winW)
  744.     current_font = i;    /* use it */
  745.     }
  746.     if (current_font == -1)    /* if didn't get a fit */
  747.       current_font = 0;        /* use smallest */
  748.  
  749.                 /* use new font */
  750.     XSetFont(dpy, gc, font_info[current_font]->fid);
  751.  
  752.                 /* set dimensions */
  753.     winW = font_widths[current_font] * TEXT_COLUMNS;
  754.     winH = winW * H_ASPECT + 
  755.        font_heights[current_font] * TEXT_LINES + MWM_BORDER;
  756.  
  757.     old_winW = winW;        /* record them */
  758.     old_winH = winH;
  759.     XResizeWindow(dpy, win, winW, winH);    /* resize window */
  760.     file_save();        /* save font */
  761.   }
  762. }
  763.  
  764. /*---------------------------------------------------------------------------*/
  765. /* set the game border around the edge of the grid - allow a redraw to
  766.  * display it */
  767. set_border()
  768. {
  769.   int i;
  770.  
  771.   for (i=0; i<GRID_SIZE; i++)
  772.   {
  773.     grid[i][0] = COL_BORDER;        /* top line */
  774.     grid[i][GRID_SIZE-1] = COL_BORDER;    /* bottom */
  775.  
  776.     grid[0][i] = COL_BORDER;        /* left */
  777.     grid[GRID_SIZE-1][i] = COL_BORDER;    /* right */
  778.   }
  779. }
  780.  
  781. /*---------------------------------------------------------------------------*/
  782. /* load all the fonts and set the height and width information */
  783. static void load_fonts()
  784. {
  785.   int i;
  786.  
  787.   for (i=0; i<NUM_FONTS; i++)    /* for all fonts */
  788.   {
  789.     if ((font_info[i] = XLoadQueryFont(dpy, font_list[i])) == NULL)
  790.     {
  791.       printf("Can't load font %s\n", font_list[i]);
  792.       exit(1);
  793.     }
  794.     font_heights[i] = 
  795.      font_info[i]->max_bounds.ascent + font_info[i]->max_bounds.descent;
  796.  
  797.     font_widths[i] = 
  798.      font_info[i]->max_bounds.rbearing - font_info[i]->min_bounds.lbearing;
  799.   }
  800. }
  801.  
  802. /*---------------------------------------------------------------------------*/
  803. /* create a window and set background to black  - also set relevant event
  804.  * masks, colours and load fonts */
  805. static void create_window()
  806. {
  807.   char *display = NULL;
  808.   XEvent event;
  809.   XSetWindowAttributes attrib;
  810.   int winX, winY, i;
  811.   XColor xcolour, screencolour;
  812.   Colormap default_cmap;
  813.  
  814. /* set display */
  815.   if(!(dpy = XOpenDisplay(display)))
  816.   {
  817.     perror("Cannot open display\n");
  818.     exit(1);
  819.   }
  820.  
  821. /* load colours */
  822.   scrn = DefaultScreenOfDisplay(dpy);
  823.   default_cmap = DefaultColormap(dpy, DefaultScreen(dpy));
  824.  
  825.   for (i=0; i<NUM_COLS; i++)
  826.   {
  827.     if (!XAllocNamedColor 
  828.             (dpy, default_cmap, colour[i], &screencolour, &xcolour))
  829.     {
  830.       printf("Colour %s not found\n", colour[i]);
  831.       exit(1);
  832.     }
  833.     colour_val[i] = xcolour.pixel;
  834.   }
  835.  
  836. /* load fonts - and set window dimensions  - to font requirements */
  837.   load_fonts();
  838.   winW = old_winW = font_widths[current_font] * TEXT_COLUMNS;
  839.   winH = old_winH = winW * H_ASPECT + 
  840.             font_heights[current_font] * TEXT_LINES + MWM_BORDER;
  841.  
  842.   winX = (WidthOfScreen(scrn) - winW) / 2;    /* centre window on screen */
  843.   winY = (HeightOfScreen(scrn) - winH) / 2;
  844.  
  845. /* set up event masks, window background col & create window */
  846.   attrib.event_mask = 
  847.     (  StructureNotifyMask     | VisibilityChangeMask  | StructureNotifyMask |
  848.        KeyPressMask     | EnterWindowMask     | LeaveWindowMask |
  849.        ExposureMask);
  850.  
  851.   attrib.background_pixel = COL_WBACK;
  852.  
  853.   win = XCreateWindow(dpy, RootWindowOfScreen(scrn), winX, winY, winW, winH,
  854.               0, DefaultDepthOfScreen(scrn), InputOutput, 
  855.               DefaultVisualOfScreen(scrn), 
  856.               CWEventMask | CWBackPixel, &attrib);
  857.  
  858. /* change title bar */
  859.   XChangeProperty(dpy, win, XA_WM_NAME, XA_STRING, 8, 
  860.           PropModeReplace, "Snake",5);
  861.   XMapWindow(dpy, win);
  862.   XFlush(dpy);
  863.   XSync(dpy,0);
  864.  
  865. /* Set up a graphics context: */
  866.   gc = XCreateGC(dpy, win, 0, NULL);
  867.   XSetFunction(dpy, gc, GXcopy);
  868.  
  869. /* set font */
  870.   XSetFont(dpy, gc, font_info[current_font]->fid);
  871.  
  872.   XFlush(dpy);
  873.   XSync(dpy,0);
  874.  
  875.   XSetBackground(dpy,gc,COL_WBACK);
  876.   XClearArea(dpy,win,0,0,winW,winH,1);
  877.   XFlush(dpy);
  878.  
  879.   for(i=0;i<100;i++)
  880.     XSync(dpy,0);
  881. }
  882.  
  883. /*---------------------------------------------------------------------------*/
  884. /* trash any accumulated events */
  885. static void flush_events()
  886. {
  887.   XEvent report;
  888.  
  889.   while(XPending(dpy))        /* while events */
  890.     XNextEvent(dpy,&report);    /* get event */
  891. }
  892.  
  893. /*---------------------------------------------------------------------------*/
  894. /* set font based on window size, set scaling, update text and scan the grid 
  895.  * redrawing all points that are not the BLANK colour */
  896. static void redraw_screen()
  897. {
  898.   int i,j;
  899.  
  900.   if (redraw_skip-- >0)        /* if just done a resize - don't redraw */
  901.     return;            /* yet - another expose will be along later */
  902.  
  903.   update_score();        /* update all text */
  904.   update_length();
  905.   update_last();
  906.   update_high();
  907.   update_max();
  908.   banner(banner_string);
  909.  
  910.   /* draw game area */
  911.   foreground(COL_DBACK);
  912.   XFillRectangle(dpy,win,gc,       drawx,drawy,draww,drawh);
  913.   XSync(dpy,0);
  914.  
  915.   for (i=0; i<GRID_SIZE; i++)        /* redraw grid */
  916.     for (j=0; j<GRID_SIZE; j++)
  917.       if (ptest(i,j))            /* if not BLANK */
  918.     pset(i,j,grid[i][j]);        /* draw it */
  919. }
  920.  
  921. /*---------------------------------------------------------------------------*/
  922. /* look for the following events:-
  923.  * window expos or size change - redraw it
  924.  * window obscured - stop game
  925.  * window visible - only start game if mouse present
  926.  * mouse enters window - start game if window visible
  927.  * mouse leaves window - stop game
  928.  * keypress - store in last_key previous key has been read
  929.  */
  930. static void check_events()
  931. {
  932.   XEvent report;
  933.   int expose = FALSE, config = FALSE;
  934.   int temp1, temp2;
  935.  
  936.   while(XPending(dpy))        /* while events */
  937.   {
  938.     XNextEvent(dpy,&report);    /* get event */
  939.  
  940.     switch(report.type)
  941.     {
  942.     case Expose:        /* window exposed */
  943.       expose = TRUE;        /* flag redraw needed */
  944.       break;
  945.  
  946.     case VisibilityNotify:    /* visibility changed */
  947.       if (report.xvisibility.state == VisibilityUnobscured)
  948.         obscured = 0;        /* say usable */
  949.                 /* - allow an Enter event to start prog */
  950.       else
  951.       {
  952.     obscured = 1;        /* say unusable */
  953.     suspend = 1;        /* and stop prog */
  954.       }
  955.       break;
  956.  
  957.     case ConfigureNotify:    /* window size changed */
  958.       winW = (int)(report.xconfigure.width);    /* get new ones */
  959.       winH = (int)(report.xconfigure.height);
  960.       config = TRUE;        /* flag redraw needed */
  961.       break;
  962.  
  963.     case KeyPress:        /* key hit */
  964.       if (last_key_hit == 0)    /* if last key was read - update it */
  965.         last_key_hit = report.xkey.keycode;
  966.       break;
  967.  
  968.     case EnterNotify:        /* pointer in window */
  969.       if (obscured == 0)    /* if window usable */
  970.     suspend = 0;        /* restart prog */
  971.       break;
  972.  
  973.     case LeaveNotify:        /* pointer out of window */
  974.       suspend = 1;        /* stop prog */
  975.       break;
  976.  
  977.     case DestroyNotify:
  978.       printf("Window destroyed\n");
  979.       exit(1);
  980.       break;
  981.  
  982.     default:
  983.       break;
  984.     }
  985.   }
  986.   if (config)        /* if window size may have changed */
  987.   {
  988.     choose_font();    /* select font to fit */
  989.     set_scales();    /* rescale */
  990.   }
  991.  
  992.   if (expose)        /* simple expose */
  993.     redraw_screen();    /* just redraw  - do after config events */
  994. }
  995.  
  996. /*---------------------------------------------------------------------------*/
  997. /* draw box at x,y on grid in colour col */
  998. static void draw_box_prim(x,y,col)
  999. int x,y,col;
  1000. {
  1001.   int i, j;
  1002.  
  1003.   for (i=0; i<BOX_WIDTH; i++)
  1004.     for (j=0; j<BOX_HEIGHT; j++)
  1005.       pset(x+i, y+j, col);
  1006. }
  1007.  
  1008. /*---------------------------------------------------------------------------*/
  1009. /* draw box at current x,y pos on grid in colour col */
  1010. static void draw_box(col)
  1011. int col;
  1012. {
  1013.   draw_box_prim(box_x_pos, box_y_pos, col);
  1014. }
  1015.  
  1016. /*---------------------------------------------------------------------------*/
  1017. /* choose a random position in the grid and if it's empty draw a box there in
  1018.  * the colour specified - returns TRUE if box created */
  1019. static int new_box(col)
  1020. int col;
  1021. {
  1022.   int i, x,y, hit = FALSE;
  1023.  
  1024.   x = get_rnd(1,GRID_SIZE - BOX_WIDTH - 1);    /* limit position to within */
  1025.   y = get_rnd(1,GRID_SIZE - BOX_HEIGHT - 1);    /* grid bounds */
  1026.  
  1027.   for (i=0; i<BOX_WIDTH; i++)            /* test x perimeter */
  1028.     if (ptest(x+i, y) || ptest(x+i, y+BOX_HEIGHT-1))
  1029.       hit = TRUE;
  1030.  
  1031.   if (!hit)
  1032.   {                        /* test y perimeter */
  1033.     for (i=0; i<BOX_HEIGHT; i++)
  1034.       if (ptest(x, y+i) || ptest(x+BOX_WIDTH-1, y+i))
  1035.     hit = TRUE;
  1036.   }
  1037.   
  1038.   if (!hit)                /* no hit */
  1039.   {
  1040.     draw_box_prim(x,y, col);        /* draw box */
  1041.     box_x_pos = x;
  1042.     box_y_pos = y;            /* record new box position */
  1043.   }
  1044.  
  1045.   return (!hit);
  1046. }
  1047.  
  1048. /*---------------------------------------------------------------------------*/
  1049. /* set some random boxes in the dead colour */
  1050. static void set_random_boxes()
  1051. {
  1052.   int count = 0;
  1053.  
  1054.   pset_inhibit = TRUE;    /* disallow drawing - allow a redraw to do this */
  1055.  
  1056.   while (count < NUM_RAND_BOXES)
  1057.   {
  1058.     if (new_box(COL_BOX_DEAD))    /* if we drew one */
  1059.       count++;            /* count it */
  1060.   }
  1061.   pset_inhibit = FALSE;
  1062. }
  1063.  
  1064. /*---------------------------------------------------------------------------*/
  1065. /* game init.
  1066.  * draw board and set up all control variables */
  1067. static void init_game()
  1068. {
  1069.   int i,j;
  1070.  
  1071.   for (i=0; i<GRID_SIZE; i++)    /* clear grid */
  1072.     for (j=0; j<GRID_SIZE; j++)
  1073.       grid[i][j] = COL_DBACK;
  1074.  
  1075.   for (i=0; i<MAX_TAIL; i++)    /* clear ring */
  1076.   {
  1077.     ring[i][0] = 0;
  1078.     ring[i][1] = 0;
  1079.   }
  1080.  
  1081.   head_ring = 3;    /* initial ring positions - initial len = 4 */
  1082.   tail_ring = 0;
  1083.  
  1084.   head_x = GRID_SIZE/2;            /* head screen position */
  1085.   head_y = GRID_SIZE/2;            /* dead centre */
  1086.  
  1087.   for (i=head_x; i<GRID_SIZE; i++)    /* pretend space in front of snake */
  1088.     grid[i][head_y] = COL_SNAKE;    /* is in use to prevent boxes */
  1089.                       /* appearing there at prog start */
  1090.  
  1091.   set_random_boxes();            /* now set boxes */
  1092.  
  1093.   for (i=head_x; i<GRID_SIZE; i++)    /* reset the space in front of snake */
  1094.     grid[i][head_y] = COL_DBACK;
  1095.  
  1096.   set_border();                /* now set border */
  1097.  
  1098.   /* place tail X and Y start coords into the ring */
  1099.   for (i=tail_ring; i<head_ring; i++)
  1100.   {
  1101.     ring[i][0] = head_x + i;         /* X coord */
  1102.     ring[i][1] = head_y;         /* Y coord */
  1103.   }
  1104.   /* NB. if we don't put tail into grid it emerges from a point at game start
  1105.    * which looks better ! */
  1106.  
  1107.   head_x += head_ring;            /* correct X coord - to actual pos */
  1108.  
  1109.   xinc = 1;                /* initial direction */
  1110.   yinc = 0;
  1111.  
  1112.   box_life = 0;                /* no box present */
  1113.   box_delay = GRID_SIZE/10;        /* delay before first box */
  1114.  
  1115.   tail_delay = 0;            /* tail moves immediately */
  1116.   score = 0;                /* no score */
  1117.   snake_len = head_ring - tail_ring;    /* actual tail length */
  1118.  
  1119.   last_key_hit = 0;            /* kill pending key hits */
  1120. }
  1121.  
  1122. /*---------------------------------------------------------------------------*/
  1123. /* move head position and test for a hit.
  1124.  * if live box hit - remove box and adjust scores.
  1125.  * if hit anything else change game state and return FALSE 
  1126.  * no hit - just draw new head pos. */
  1127. static int move_head()
  1128. {
  1129.   int retval = TRUE;
  1130.  
  1131.   head_x += xinc;        /* move head */
  1132.   head_y += yinc;
  1133.  
  1134.   if (ptest(head_x, head_y))    /* has it hit */
  1135.   {
  1136.                 /* is it the live box */
  1137.     if (head_x >= box_x_pos && head_x <= box_x_pos + BOX_WIDTH && 
  1138.     head_y >= box_y_pos && head_y <= box_y_pos + BOX_HEIGHT && box_life>0)
  1139.     {
  1140.       tail_delay = box_value;        /* extend tail by new amount */
  1141.       snake_len += box_value;        /* add to length */
  1142.       score += box_value;        /* add to score */
  1143.                     /* if box was dangerous */
  1144.       if (box_remain && box_life <= BOX_WARNING)
  1145.     score += 10;            /* bonus points */
  1146.  
  1147.       box_life = 0;            /* box is dead */
  1148.       draw_box(COL_DBACK);        /* blank it */
  1149.  
  1150.       update_length();            /* display new length * score */
  1151.       update_score();
  1152.     }
  1153.     else                /* not live box - you died */
  1154.       retval = FALSE;            /* say dead */
  1155.   }
  1156.   if (retval)                /* if not dead */
  1157.   {
  1158.     pset(head_x, head_y, COL_SNAKE);    /* draw new head pos */
  1159.  
  1160.     ring[head_ring][0] = head_x;    /* store new head pos in ring */
  1161.     ring[head_ring][1] = head_y;
  1162.  
  1163.     if (++head_ring > tail_max)        /* wrap head to ring start */
  1164.       head_ring = 0;
  1165.   }
  1166.  
  1167.   return retval;
  1168. }
  1169.  
  1170. /*---------------------------------------------------------------------------*/
  1171. /* move tail and erase old tail position */
  1172. static void move_tail()
  1173. {
  1174.   if (tail_delay >0)            /* if tail growing - exit */
  1175.   {
  1176.     tail_delay--;
  1177.     return;
  1178.   }
  1179.                     /* erase old position */
  1180.   pset(ring[tail_ring][0], ring[tail_ring][1], COL_DBACK);
  1181.  
  1182.   if (++tail_ring > tail_max)         /* wrap tail to ring start */
  1183.     tail_ring = 0;
  1184.  
  1185.   if (tail_ring == head_ring)        /* if tail hits head - delay it */
  1186.     tail_delay = 1;            /* shouldn't happen - just incase */
  1187. }
  1188.  
  1189. /*---------------------------------------------------------------------------*/
  1190. /* if box is dead and time for a new one draw it and set up box values.
  1191.  * if alive then see if its time for a warning or time to remove it */
  1192. static void eval_box()
  1193. {
  1194.   if (box_life > 0)            /* if box alive */
  1195.   {
  1196.     --box_life;
  1197.                     /* if will remain and warning due */
  1198.     if (box_life == BOX_WARNING && box_remain)
  1199.       draw_box(COL_BOX_WARN);        /* show warning colour */
  1200.     
  1201.     if (box_life == 0)            /* if box life ended */
  1202.     {
  1203.       if (box_remain)            /* and will remain */
  1204.     draw_box(COL_BOX_DEAD);        /* draw dead box */
  1205.       else
  1206.       {
  1207.         draw_box(COL_DBACK);        /* wont remain - erase it */
  1208.     score -= 10;            /* lose ten points */
  1209.         update_score();            /* show new score */
  1210.       }
  1211.     }
  1212.   }
  1213.   else                    /* box dead */
  1214.   {
  1215.     if (--box_delay <= 0)        /* is it time for a new one */
  1216.     {
  1217.       if (new_box(COL_BOX_LIVE))    /* draw new box */
  1218.       {
  1219.     /* box life value allows you to move 1/2 to 1 and 1/4 across grid
  1220.      * before box dissapears */
  1221.     box_life = get_rnd(GRID_SIZE/2, GRID_SIZE+GRID_SIZE/4);
  1222.  
  1223.         /* 40% chance of box remaining */
  1224.     box_remain = (get_rnd(1,100) < 40) ? TRUE : FALSE;
  1225.  
  1226.     /* delay before new box allows you to move 1/4 to 3/4 across grid
  1227.      * before new box arrives */
  1228.     box_delay = get_rnd(GRID_SIZE/4,GRID_SIZE/2+GRID_SIZE/4);
  1229.  
  1230.     /* box value grows snake by 1/10 to 1/4 of grid size */
  1231.     box_value = get_rnd(GRID_SIZE/10,GRID_SIZE/4);
  1232.       }
  1233.     }
  1234.   }
  1235. }
  1236.  
  1237. /*---------------------------------------------------------------------------*/
  1238. /* zero key store and return the key - if any */
  1239. static int get_key()
  1240. {
  1241.   int temp;
  1242.  
  1243.   temp = last_key_hit;        /* get key */
  1244.   last_key_hit = 0;        /* zero store */
  1245.  
  1246.   return temp;
  1247. }
  1248.  
  1249. /*---------------------------------------------------------------------------*/
  1250. /* get key hit and adjust direction */
  1251. static void direction_check()
  1252. {
  1253.   int i, key, match = FALSE;
  1254.  
  1255.   if (key = get_key())            /* get key */
  1256.   {
  1257.     if (key == KEY_ESC)            /* abort prog */
  1258.     {
  1259.       game_state = S_EXIT;
  1260.       return;
  1261.     }
  1262.  
  1263.     for (i=0; i<4; i++)            /* for all directions */
  1264.     {
  1265.       if (key_list[i] == key)        /* was it this key */
  1266.       {
  1267.     match = TRUE;            /* yes */
  1268.         break;
  1269.       }
  1270.     }
  1271.     if (match)                /* if we matched a key */
  1272.     {
  1273.       switch (i)            /* depending on direction */
  1274.       {                    /* set increments */
  1275.       case KEY_DIR_UP:
  1276.       case KEY_DIR_DOWN:
  1277.     xinc = 0;                /* no X movement */
  1278.     yinc = i == KEY_DIR_UP ? -1 : 1;    /* set Y direction */
  1279.     break;
  1280.  
  1281.       case KEY_DIR_LEFT:
  1282.       case KEY_DIR_RIGHT:
  1283.     yinc = 0;                /* no Y movement */
  1284.     xinc = i == KEY_DIR_LEFT ? -1 : 1;    /* set X direction */
  1285.     break;
  1286.  
  1287.       default:
  1288.     break;
  1289.       }
  1290.     }
  1291.   }
  1292. }
  1293.  
  1294. /*---------------------------------------------------------------------------*/
  1295. /* wait for key press and return it */
  1296. static int wait_key()
  1297. {
  1298.   int key;
  1299.  
  1300.   while((key = get_key()) == 0)        /* wait for key hit */
  1301.   {
  1302.     nap(PAUSE);
  1303.     check_events();
  1304.   }
  1305.  
  1306.   return key;
  1307. }
  1308.  
  1309. /*---------------------------------------------------------------------------*/
  1310. /* display usage message  NB. don't document the -/ option */
  1311. static void usage()
  1312. {
  1313.   printf("Usage: xsnake [-lx] [-sx] [-h]\n\n");
  1314.  
  1315.   printf(
  1316. "-l  Set max tail length to x, where %d <= x <= %d (modulo %d).\n", 
  1317.                         MIN_TAIL, MAX_TAIL, INC_TAIL);
  1318.   printf("-s  Set window scale to x, where 1 <= x <= %d.\n", NUM_FONTS);
  1319.   printf("-h  Display help information.\n\n");
  1320.   exit(1);
  1321. }
  1322.  
  1323. /*---------------------------------------------------------------------------*/
  1324. /* display the help text - with paging */
  1325. static void help()
  1326. {
  1327.   char **s1;
  1328.  
  1329.   s1 = help_text;
  1330.  
  1331.   while (*s1)
  1332.     printf("%s\n", *s1++);
  1333.  
  1334.   exit(1);
  1335. }
  1336.  
  1337. /*---------------------------------------------------------------------------*/
  1338. /* prog init and game control */
  1339. main(argc, argv)
  1340. int argc;
  1341. char **argv;
  1342. {
  1343.   int key, i, suspend_noted = FALSE, stop, user_len = 0, user_size = 0;
  1344.   char s1[TEXT_COLUMNS];
  1345.  
  1346.   if (argc >1)
  1347.   {
  1348.     while (++argv, --argc)    /* scan for args */
  1349.     {
  1350.       switch(argv[0][1])
  1351.       {
  1352.       case 'l':            /* length change - mod it to INC_TAIL */
  1353.     user_len = atoi(&argv[0][2]);
  1354.     if (user_len < MIN_TAIL || user_len > MAX_TAIL)
  1355.       usage();
  1356.     user_len = user_len - user_len % 50;
  1357.     break;
  1358.       case 's':            /* size change */
  1359.     user_size = atoi(&argv[0][2]);
  1360.     if (user_size <1 || user_size > NUM_FONTS)
  1361.       usage();
  1362.     break;
  1363.       case 'h':                      /* paged help - cheat by using */
  1364.     if (system("xsnake -/ | more") != 0)   /* "snake -/" piped to more */
  1365.       help();        /* command failed - show it unpaged */
  1366.     exit(1);
  1367.     break;
  1368.       case '/':            /* just output help text */
  1369.     help();
  1370.     break;
  1371.       default:
  1372.     usage();
  1373.       }
  1374.     }
  1375.   }
  1376.  
  1377.   find_file();            /* read keys, tail len, font and high score */
  1378.  
  1379.   if (user_len)            /* user overrides */
  1380.     tail_max = user_len;
  1381.   if (user_size)
  1382.     current_font = user_size -1;
  1383.  
  1384.   if (user_len || user_size)
  1385.     file_save();        /* store user changes */
  1386.  
  1387.   pset_inhibit = FALSE;        /* don't set points yet */
  1388.   redraw_skip = 0;        /* allow redraw */
  1389.   game_state = S_INIT;        /* initial state */
  1390.   suspend = TRUE;        /* stop - until mouse enters first time */
  1391.   score_last = 0;        /* no last score */
  1392.  
  1393.   create_window();        /* create window and set scaling */
  1394.   col_max_len = COL_FONT_NORM;  /* set max len colour to normal - do after */
  1395.                 /* colour load */
  1396.   set_scales();            /* set scales to initial window size */
  1397.   seed_rnd();            /* set random number generator */
  1398.   flush_events();        /* trash any pending events */
  1399.                 /* we get lots at window create time */
  1400.  
  1401.   for(;;)            /* forever */
  1402.   {
  1403.     switch (game_state)        /* on game state */
  1404.     {
  1405.     case S_INIT:        /* game init */
  1406.       banner(" ");        /* clear banner line */
  1407.       init_game();        /* set up board etc */
  1408.       redraw_screen();        /* show screen */
  1409.       game_state = S_RUN;    /* run it */
  1410.       break;
  1411.  
  1412.     case S_RUN:            /* game running */
  1413.       if (!suspend)        /* if not stopped */
  1414.       {
  1415.     if (suspend_noted)    /* if banner on */
  1416.     {
  1417.       banner(" ");        /* clear it */
  1418.       suspend_noted = FALSE;/* allow banner print */
  1419.     }
  1420.  
  1421.     if (move_head())    /* if havent died */
  1422.     {
  1423.       move_tail();        /* move tail - box and test keys */
  1424.       eval_box();
  1425.       direction_check();
  1426.     }
  1427.     else            /* have died */
  1428.     {
  1429.       banner("* H I T  -  Press SPACE *");
  1430.       game_state = S_SPACE;    /* change state */
  1431.     }
  1432.       }                /* stopped */
  1433.       else
  1434.       {
  1435.     if (!suspend_noted)    /* if banner not on */
  1436.     {
  1437.       banner("Game Paused");
  1438.       suspend_noted = TRUE;    /* disallow banner print */
  1439.     }
  1440.  
  1441.     if (get_key() == KEY_ESC)/* flush keys and test for esc */
  1442.       game_state = S_EXIT;    /* exit prog - even when paused */
  1443.       }
  1444.       break;
  1445.  
  1446.     case S_SPACE:        /* waiting for space hit */
  1447.       get_key();        /* throw away any spurious spaces */
  1448.       if ((key = wait_key()) == KEY_ESC)
  1449.     game_state = S_EXIT;    /* if ESC - exit */
  1450.       else
  1451.     if (key == KEY_SPACE)
  1452.     {
  1453.       if (score == SCORE_HIGH && score != 0)/* if same as high and */
  1454.           {                    /* worth a mention */
  1455.         banner("Matched High Score !");
  1456.         flash_banner();
  1457.       }
  1458.       if (score > SCORE_HIGH)    /* if new high */
  1459.       {
  1460.         banner("New High Score !");
  1461.         flash_banner();
  1462.         SCORE_HIGH = score;    /* update high - but don't display it yet */
  1463.         file_save();        /* save high score */
  1464.       }
  1465.       score_last = score;        /* update last */
  1466.  
  1467.       game_state = S_CONT;        /* if space - cont */
  1468.       banner("Any key for new game, CR to change config");
  1469.     }
  1470.       break;
  1471.  
  1472.     case S_CONT:        /* waiting for continue or configure */
  1473.       get_key();        /* chuck spurious keys */
  1474.       if ((key = wait_key()) == KEY_ESC)
  1475.         game_state = S_EXIT;    /* if esc - exit */
  1476.       else
  1477.     if (key == KEY_CR)
  1478.     {
  1479.       game_state = S_CONFIG;/* if cr - config */
  1480.       banner("Length, Key change or cancel (press L,K or Space)");
  1481.     }
  1482.     else
  1483.       game_state = S_INIT;    /* other - then new game */
  1484.       break;
  1485.  
  1486.     case S_CONFIG:        /* waiting for config type */
  1487.       get_key();        /* chuck spurious keys */
  1488.       if ((key = wait_key()) == KEY_L)
  1489.     game_state = S_SET_LEN;    /* if L - then set length */
  1490.       else
  1491.     if (key == KEY_K)    /* if K - then set keys */
  1492.       game_state = S_SET_KEY;
  1493.     else
  1494.       if (key == KEY_ESC)    /* if esc - exit */
  1495.         game_state = S_EXIT;
  1496.       else
  1497.         game_state = S_INIT;/* other - abort config - new game */
  1498.       break;
  1499.       
  1500.     case S_SET_LEN:        /* set length */
  1501.       stop = FALSE;        /* don't exit yet */
  1502.       banner("Press < to decrease or > to increase, CR to set");
  1503.       col_max_len = COL_FONT_CHNG;/* highligh max length */
  1504.       update_max();        /* draw it in new colour */
  1505.       while(!stop)
  1506.       {
  1507.     key = get_key();    /* grab key */
  1508.     switch(key)
  1509.     {
  1510.     case KEY_LESS:        /* if a < key - dec tail */
  1511.       tail_max -= INC_TAIL;    /* limit to MIN_TAIL */
  1512.       if (tail_max < MIN_TAIL)
  1513.         tail_max = MIN_TAIL;
  1514.       update_max();        /* disp new value */
  1515.       update_high();    /* and associated high score */
  1516.       break;
  1517.     case KEY_MORE:        /* if a > key - inc tail */
  1518.       tail_max += INC_TAIL;    /* limit to MAX_TAIL */
  1519.       if (tail_max > MAX_TAIL)
  1520.         tail_max = MAX_TAIL;
  1521.       update_max();        /* disp new value */
  1522.       update_high();    /* and associated high score */
  1523.       break;
  1524.     case KEY_CR:
  1525.       col_max_len = COL_FONT_NORM;/* max len colour back to normal */
  1526.       file_save();        /* save new len */
  1527.       game_state = S_INIT;    /* new game */
  1528.       stop = TRUE;        /* exit loop */
  1529.       break;
  1530.     case KEY_ESC:        /* exit prog */
  1531.       game_state = S_EXIT;
  1532.       stop = TRUE;
  1533.       break;
  1534.     default:
  1535.       break;
  1536.     }
  1537.     nap(PAUSE);
  1538.     check_events();    /* scan for keys etc */
  1539.       }
  1540.       break;
  1541.  
  1542.     case S_SET_KEY:        /* key change */
  1543.       get_key();        /* chuck spurious keys */
  1544.       for (i=0; i<4; i++)    /* for the four directions */
  1545.       {
  1546.     sprintf(s1, "Press the new %s key", key_dir[i]);
  1547.     banner(s1);
  1548.     key = wait_key();
  1549.  
  1550.     if (key == KEY_ESC)    /* if esc - exit */
  1551.     {
  1552.       game_state = S_EXIT;
  1553.       break;
  1554.     }
  1555.         key_list[i] = key;    /* store key press */
  1556.       }
  1557.       if (game_state != S_EXIT)    /* if got keys ok then new game */
  1558.       {
  1559.     file_save();        /* save keys */
  1560.     game_state = S_INIT;
  1561.       }
  1562.       break;
  1563.  
  1564.     case S_EXIT:        /* game exit */
  1565.       exit(0);
  1566.       break;
  1567.  
  1568.     default:
  1569.       printf("Game state error\n");
  1570.       exit(1);
  1571.     }
  1572.     check_events();
  1573.     nap(PAUSE);
  1574.   }
  1575. }
  1576. /* ends */
  1577.